/*
 * @(#)DataSourceFactory.java
 *
 * Copyright (c) 2003 DCIVision Ltd
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of DCIVision
 * Ltd ("Confidential Information").  You shall not disclose such Confidential
 * Information and shall use it only in accordance with the terms of the license
 * agreement you entered into with DCIVision Ltd.
 */
package com.dcivision.framework;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Savepoint;
import java.sql.Statement;
import java.util.Date;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.impl.LogFactoryImpl;

/**
 * Wrapper class to wrap the original java.sql.Connection This class is used for
 * monitoring SQL activities and connection leaking investigation The main
 * usages: 1) Capture the last SQL statement sent by this connection 2) Capture
 * the execution time of the connection 3) Printout warning to alert the system
 * that this connection is not closed properly 4) Printout warning to alert long
 * running SQL statements
 * 
 * @author Ryan Kwan
 * @company DCIVision Limited
 * @creation date 24/06/2003
 * @version $Revision: 1.9 $
 */
public class DebugConnection implements Connection {

    public static final int DEBUG_THREAD_NAME = 1;

    public static final int DEUBG_PRINT_ALL_SQL = 2;

    public static final String REVISION = "$Revision: 1.9 $";

    protected Log logger = new LogFactoryImpl().getInstance(this.getClass());

    private long crtime;

    private static final long CON_THRESHOLD = 10L;

    private Connection mConn;

    private int mDebugLevel;

    private String firstSQLQuery;

    private String lastSQLQuery;

    private boolean isClosed = false;

    private long uid = System.currentTimeMillis();

    public DebugConnection(Connection connection, int debugLevel) {
        lastSQLQuery = "";
        mDebugLevel = debugLevel;
        mConn = connection;
        crtime = (new Date()).getTime();
        StringBuffer buf = new StringBuffer("***CONNECTION FETCH*** ");
        buf.append(" ID: [");
        buf.append(uid);
        buf.append("], Connection : [");
        buf.append(mConn);
        buf.append("], Thread:[");
        buf.append(Thread.currentThread().getName());
        buf.append("], ");
        logger.debug(buf.toString());
        buf = null;
    }

    public void finalize() {
        try {
            //check the connection is closed manually, if isClosed=false, that means the  
            //connection is force closed by the pool
            if (!isClosed) {
                double holdsec = (double) ((new Date()).getTime() - crtime) / 1000D;
                StringBuffer buf = new StringBuffer(
                        "***CONNECTION CLOSED BY SERVER*** ");
                buf.append(" ID: [");
                buf.append(uid);
                buf.append("], Connection : [");
                buf.append(mConn);
                buf.append("], Thread:[");
                buf.append(Thread.currentThread().getName());
                buf.append("], ");
                if(mDebugLevel >= DEUBG_PRINT_ALL_SQL){
                  buf.append(" Last SQL: [");
                  buf.append(lastSQLQuery);
                  buf.append("],");
                } 
                logger.error(buf.toString());
                buf = null;
                if(mConn!=null) mConn.close();
		isClosed = true;
            }
        } catch (SQLException e) {
            logger.error(e);
        }
        mConn = null;
    }

    private void logSQLQuery(String sql){
	if(firstSQLQuery==null) firstSQLQuery=sql;
	lastSQLQuery=sql;
    }

    public PreparedStatement prepareStatement(String sql) throws SQLException {
        logSQLQuery(sql);
        return mConn.prepareStatement(sql);
    }

    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        logSQLQuery(sql);
        return mConn.prepareStatement(sql, resultSetType, resultSetConcurrency);
    }
 
    public CallableStatement prepareCall(String sql) throws SQLException {
        logSQLQuery(sql);
        return mConn.prepareCall(sql);
    }

    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        logSQLQuery(sql);
        return mConn.prepareCall(sql, resultSetType, resultSetConcurrency);
    }

    public void close() throws SQLException {
        StringBuffer buf = new StringBuffer("***CONNECTION CLOSE*** ");
        double holdsec = (double) ((new Date()).getTime() - crtime) / 1000D;
        buf.append(" ID: [");
        buf.append(uid);
        buf.append("], Connection : [");
        buf.append(mConn);
        buf.append("], Thread:[");
        buf.append(Thread.currentThread().getName());
        buf.append("], ");
        if(mDebugLevel >= DEUBG_PRINT_ALL_SQL){
            buf.append(" Last SQL: [");
            buf.append(lastSQLQuery);
            buf.append("],");
        }        
        buf.append(" On Hold: [");
        buf.append(holdsec);
        buf.append(" sec]");
        logger.debug(buf.toString());
        buf = null;

        if (holdsec > CON_THRESHOLD)
            logger.warn("***CONNECTION HAS BEEN HELD LONGER THAN "
                    + CON_THRESHOLD + " sec ***");
	if(mConn!=null) mConn.close();
	isClosed=true;
	mConn = null;
    }

    public SQLWarning getWarnings() throws SQLException {
        return mConn.getWarnings();
    }

    public void clearWarnings() throws SQLException {
        mConn.clearWarnings();
    }

    public Statement createStatement() throws SQLException {
        return mConn.createStatement();
    }

    public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
        return mConn.createStatement(resultSetType, resultSetConcurrency);
    }

    public Map getTypeMap() throws SQLException {
        return mConn.getTypeMap();
    }

    public void setTypeMap(Map map) throws SQLException {
        mConn.setTypeMap(map);
    }

    public String nativeSQL(String sql) throws SQLException {
        logSQLQuery(sql);
        return mConn.nativeSQL(sql);
    }

    public void setAutoCommit(boolean autoCommit) throws SQLException {
        mConn.setAutoCommit(autoCommit);
    }

    public boolean getAutoCommit() throws SQLException {
        return mConn.getAutoCommit();
    }

    public void commit() throws SQLException {
        mConn.commit();
    }

    public void rollback() throws SQLException {
        mConn.rollback();
    }

    public boolean isClosed() throws SQLException {
        return isClosed;
    }

    public DatabaseMetaData getMetaData() throws SQLException {
        return mConn.getMetaData();
    }

    public void setReadOnly(boolean readOnly) throws SQLException {
        mConn.setReadOnly(readOnly);
    }

    public boolean isReadOnly() throws SQLException {
        return mConn.isReadOnly();
    }

    public void setCatalog(String catalog) throws SQLException {
        mConn.setCatalog(catalog);
    }

    public String getCatalog() throws SQLException {
        return mConn.getCatalog();
    }

    public void setTransactionIsolation(int level) throws SQLException {
        mConn.setTransactionIsolation(level);
    }

    public int getTransactionIsolation() throws SQLException {
        return mConn.getTransactionIsolation();
    }

    public int getHoldability() throws SQLException {
        return mConn.getHoldability();
    }

    public void setHoldability(int arg0) throws SQLException {
        mConn.setHoldability(arg0);
    }

    public Savepoint setSavepoint() throws SQLException {
        return mConn.setSavepoint();
    }

    public void releaseSavepoint(Savepoint arg0) throws SQLException {
        mConn.releaseSavepoint(arg0);
    }

    public void rollback(Savepoint arg0) throws SQLException {
        mConn.rollback(arg0);
    }

    public Statement createStatement(int arg0, int arg1, int arg2) throws SQLException {
        return mConn.createStatement(arg0, arg1, arg2);
    }

    public CallableStatement prepareCall(String arg0, int arg1, int arg2, int arg3) throws SQLException {
        return mConn.prepareCall(arg0, arg1, arg2, arg3);
    }

    public PreparedStatement prepareStatement(String arg0, int arg1)
            throws SQLException {
        return mConn.prepareStatement(arg0, arg1);
    }

    public PreparedStatement prepareStatement(String arg0, int arg1, int arg2,
            int arg3) throws SQLException {
        return mConn.prepareStatement(arg0, arg1, arg2, arg3);
    }

    public PreparedStatement prepareStatement(String arg0, int[] arg1) throws SQLException {
        return mConn.prepareStatement(arg0, arg1);
    }

    public Savepoint setSavepoint(String arg0) throws SQLException {
        return mConn.setSavepoint(arg0);
    }

    public PreparedStatement prepareStatement(String arg0, String[] arg1) throws SQLException {
        return prepareStatement(arg0, arg1);
    }

    public String getFirstSQLQuery(){
      return lastSQLQuery;
    }

    public String getLastSQLQuery(){
      return lastSQLQuery;
    }
}